GtkGLArea: Major reworking
authorAlexander Larsson <alexl@redhat.com>
Thu, 30 Oct 2014 17:22:44 +0000 (18:22 +0100)
committerAlexander Larsson <alexl@redhat.com>
Thu, 30 Oct 2014 20:04:29 +0000 (21:04 +0100)
This restructures the way buffers are allocated and bound in a way
that is more flexible.

Buffer operation happens in three phases:
create_buffer() - Creates the gl objects
allocate_buffers() - Allocates space for the buffers at a given size
attach_buffers() - Attaches the buffers to the framebuffer and makes
                   the framebuffer the default drawing target
And destroy via
delete_buffers()

We call all these the first draw, and after that we allocate buffers
each time the widget changes size until the buffers are deleted.
We delete the buffers at unrealize.

However, anyone that wants to manually control buffer allocation strategies
can manually call allocate/delete_buffers().

There are also some other changes:
* Support for stencil buffers.
* A manual render mode where ::draw doesn't render unless you manually
  invalidated the previous rendering.

gtk/gtkglarea.c
gtk/gtkglarea.h

index 5d4cac32031541d4f25ce2a8b78a070013ef9895..51fdff97d93e032fc848dd3af6f6b4dfa961c016 100644 (file)
 typedef struct {
   GdkGLContext *context;
   GError *error;
-  GLuint framebuffer;
+
+  gboolean have_buffers;
+
+  guint frame_buffer;
+  guint render_buffer;
+  guint texture;
+  guint depth_stencil_buffer;
+
   gboolean has_alpha;
   gboolean has_depth_buffer;
+  gboolean has_stencil_buffer;
+
+  gboolean needs_render;
+  gboolean auto_render;
 } GtkGLAreaPrivate;
 
 enum {
@@ -131,7 +142,9 @@ enum {
   PROP_CONTEXT,
   PROP_HAS_ALPHA,
   PROP_HAS_DEPTH_BUFFER,
+  PROP_HAS_STENCIL_BUFFER,
 
+  PROP_AUTO_RENDER,
   LAST_PROP
 };
 
@@ -143,6 +156,8 @@ enum {
   LAST_SIGNAL
 };
 
+static void gtk_gl_area_allocate_buffers (GtkGLArea *area, int width, int height);
+
 static guint area_signals[LAST_SIGNAL] = { 0, };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkGLArea, gtk_gl_area, GTK_TYPE_WIDGET)
@@ -150,8 +165,8 @@ G_DEFINE_TYPE_WITH_PRIVATE (GtkGLArea, gtk_gl_area, GTK_TYPE_WIDGET)
 static void
 gtk_gl_area_dispose (GObject *gobject)
 {
-  GtkGLArea *self = GTK_GL_AREA (gobject);
-  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
+  GtkGLArea *area = GTK_GL_AREA (gobject);
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
 
   g_clear_object (&priv->context);
 
@@ -166,6 +181,11 @@ gtk_gl_area_set_property (GObject      *gobject,
 {
   switch (prop_id)
     {
+    case PROP_AUTO_RENDER:
+      gtk_gl_area_set_auto_render (GTK_GL_AREA(gobject),
+                                  g_value_get_boolean (value));
+      break;
+
     case PROP_HAS_ALPHA:
       gtk_gl_area_set_has_alpha (GTK_GL_AREA(gobject),
                                  g_value_get_boolean (value));
@@ -176,6 +196,11 @@ gtk_gl_area_set_property (GObject      *gobject,
                                         g_value_get_boolean (value));
       break;
 
+    case PROP_HAS_STENCIL_BUFFER:
+      gtk_gl_area_set_has_stencil_buffer (GTK_GL_AREA(gobject),
+                                        g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     }
@@ -191,6 +216,10 @@ gtk_gl_area_get_property (GObject    *gobject,
 
   switch (prop_id)
     {
+    case PROP_AUTO_RENDER:
+      g_value_set_boolean (value, priv->auto_render);
+      break;
+
     case PROP_HAS_ALPHA:
       g_value_set_boolean (value, priv->has_alpha);
       break;
@@ -199,6 +228,10 @@ gtk_gl_area_get_property (GObject    *gobject,
       g_value_set_boolean (value, priv->has_depth_buffer);
       break;
 
+    case PROP_HAS_STENCIL_BUFFER:
+      g_value_set_boolean (value, priv->has_stencil_buffer);
+      break;
+
     case PROP_CONTEXT:
       g_value_set_object (value, priv->context);
       break;
@@ -220,32 +253,212 @@ gtk_gl_area_realize (GtkWidget *widget)
   priv->context = gdk_window_create_gl_context (window,
                                                 GDK_GL_PROFILE_DEFAULT,
                                                 &priv->error);
-  if (priv->context != NULL)
+}
+
+/*
+ * Creates all the buffer objects needed for rendering the scene
+ */
+static void
+gtk_gl_area_create_buffers (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+  GtkWidget *widget = GTK_WIDGET (area);
+  int scale;
+
+  gtk_widget_realize (widget);
+
+  if (priv->context == NULL)
+    return;
+
+  if (priv->have_buffers)
+    return;
+
+  priv->have_buffers = TRUE;
+
+  glGenFramebuffersEXT (1, &priv->frame_buffer);
+
+  if (priv->has_alpha)
+    /* For alpha we use textures as that is required for blending to work */
+    glGenTextures (1, &priv->texture);
+  else
+    /* For non-alpha we use render buffers so we can blit instead of texture the result */
+    glGenRenderbuffersEXT (1, &priv->render_buffer);
+
+  if (priv->has_depth_buffer || priv->has_stencil_buffer)
+    glGenRenderbuffersEXT (1, &priv->depth_stencil_buffer);
+
+  scale = gtk_widget_get_scale_factor (widget);
+  gtk_gl_area_allocate_buffers (area,
+                               gtk_widget_get_allocated_width (widget) * scale,
+                               gtk_widget_get_allocated_height (widget) * scale);
+}
+
+/*
+ * Allocates space of the right type and size for all the buffers
+ */
+static void
+gtk_gl_area_allocate_buffers (GtkGLArea *area, int width, int height)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  if (priv->context == NULL)
+    return;
+
+  if (priv->texture)
+    {
+      glBindTexture (GL_TEXTURE_2D, priv->texture);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+      glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+    }
+
+  if (priv->render_buffer)
+    {
+      glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, priv->render_buffer);
+      glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGB8, width, height);
+    }
+
+  if (priv->has_depth_buffer || priv->has_stencil_buffer)
     {
-      gdk_gl_context_make_current (priv->context);
-      glGenFramebuffersEXT (1, &priv->framebuffer);
-      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, priv->framebuffer);
+      glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, priv->depth_stencil_buffer);
+      if (priv->has_stencil_buffer)
+       glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, width, height);
+      else
+       glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);
     }
 }
 
+static void
+gtk_gl_area_maybe_allocate_buffers (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+  GtkWidget *widget = GTK_WIDGET (area);
+  int scale;
+
+  if (!priv->have_buffers)
+    return;
+
+  gtk_gl_area_make_current (area);
+  scale = gtk_widget_get_scale_factor (widget);
+  gtk_gl_area_allocate_buffers (area,
+                               gtk_widget_get_allocated_width (widget) * scale,
+                               gtk_widget_get_allocated_height (widget) * scale);
+}
+
+/**
+ * gtk_gl_area_attach_buffers:
+ * @area: a #GtkGLArea
+ *
+ * Ensures that the @area framebuffer object is made the current draw
+ * and read target, and that all the required buffers for the @area
+ * are created and bound to the frambuffer.
+ *
+ * This function is automatically called before emitting the
+ * #GtkGLArea::render signal, and doesn't normally need to be called
+ * by application code.
+ *
+ * Since: 3.16
+ */
+void
+gtk_gl_area_attach_buffers (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  gtk_gl_area_make_current (area);
+
+  if (!priv->have_buffers)
+    gtk_gl_area_create_buffers (area);
+
+  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, priv->frame_buffer);
+
+  if (priv->texture)
+    glFramebufferTexture2D (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                           GL_TEXTURE_2D, priv->texture, 0);
+  else if (priv->render_buffer)
+    glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                                 GL_RENDERBUFFER_EXT, priv->render_buffer);
+
+  if (priv->depth_stencil_buffer)
+    {
+      if (priv->has_depth_buffer)
+       glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                     GL_RENDERBUFFER_EXT, priv->depth_stencil_buffer);
+      if (priv->has_stencil_buffer)
+       glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                     GL_RENDERBUFFER_EXT, priv->depth_stencil_buffer);
+    }
+}
+
+static void
+gtk_gl_area_delete_buffers (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  if (priv->context == NULL)
+    return;
+
+  if (!priv->have_buffers)
+    return;
+
+  priv->have_buffers = FALSE;
+
+  if (priv->render_buffer != 0)
+    {
+      glDeleteRenderbuffersEXT(1, &priv->render_buffer);
+      priv->render_buffer = 0;
+    }
+
+  if (priv->texture != 0)
+    {
+      glDeleteTextures(1, &priv->texture);
+      priv->texture = 0;
+    }
+
+  if (priv->depth_stencil_buffer != 0)
+    {
+      glDeleteRenderbuffersEXT(1, &priv->depth_stencil_buffer);
+
+      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
+      glDeleteFramebuffersEXT (1, &priv->frame_buffer);
+      priv->frame_buffer = 0;
+    }
+}
+
+static void
+gtk_gl_area_post_render (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+  GLenum invalidate[4];
+  int i = 0;
+
+  if (priv->has_depth_buffer)
+    invalidate[i++] = GL_DEPTH_ATTACHMENT;
+
+  if (priv->has_stencil_buffer)
+    invalidate[i++] = GL_STENCIL_ATTACHMENT;
+
+  if (priv->auto_render)
+    invalidate[i++] = GL_COLOR_ATTACHMENT0;
+
+  glInvalidateFramebuffer (GL_FRAMEBUFFER, i, invalidate);
+}
+
+
 static void
 gtk_gl_area_unrealize (GtkWidget *widget)
 {
-  GtkGLArea *self = GTK_GL_AREA (widget);
-  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
+  GtkGLArea *area = GTK_GL_AREA (widget);
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
 
   if (priv->context != NULL)
     {
-      if (priv->framebuffer != 0)
+      if (priv->have_buffers)
        {
-         gtk_gl_area_make_current (self);
-         /* Bind 0, which means render to back buffer, as a result, fb is unbound */
-         glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
-         glDeleteFramebuffersEXT (1, &priv->framebuffer);
-         priv->framebuffer = 0;
+         gtk_gl_area_make_current (area);
+         gtk_gl_area_delete_buffers (area);
        }
-      else
-       g_warning ("can't free framebuffer");
 
       /* Make sure to destroy if current */
       g_object_run_dispose (G_OBJECT (priv->context));
@@ -262,25 +475,31 @@ static void
 gtk_gl_area_size_allocate (GtkWidget     *widget,
                            GtkAllocation *allocation)
 {
+  GtkGLArea *area = GTK_GL_AREA (widget);
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
   GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->size_allocate (widget, allocation);
+
+  priv->needs_render = TRUE;
+  gtk_gl_area_maybe_allocate_buffers (area);
 }
 
 static void
-gtk_gl_area_draw_error_screen (GtkGLArea *self,
+gtk_gl_area_draw_error_screen (GtkGLArea *area,
                                cairo_t   *cr,
                                gint       width,
                                gint       height)
 {
-  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
   PangoLayout *layout;
   int layout_height;
 
-  layout = gtk_widget_create_pango_layout (GTK_WIDGET (self),
+  layout = gtk_widget_create_pango_layout (GTK_WIDGET (area),
                                            priv->error->message);
   pango_layout_set_width (layout, width * PANGO_SCALE);
   pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
   pango_layout_get_pixel_size (layout, NULL, &layout_height);
-  gtk_render_layout (gtk_widget_get_style_context (GTK_WIDGET (self)),
+  gtk_render_layout (gtk_widget_get_style_context (GTK_WIDGET (area)),
                      cr,
                      0, (height - layout_height) / 2,
                      layout);
@@ -292,93 +511,57 @@ static gboolean
 gtk_gl_area_draw (GtkWidget *widget,
                   cairo_t   *cr)
 {
-  GtkGLArea *self = GTK_GL_AREA (widget);
-  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
+  GtkGLArea *area = GTK_GL_AREA (widget);
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
   gboolean unused;
   int w, h, scale;
-  GLuint color_rb = 0, depth_rb = 0, color_tex = 0;
   GLenum status;
 
   if (priv->context == NULL)
     {
-      gtk_gl_area_draw_error_screen (self,
+      gtk_gl_area_draw_error_screen (area,
                                      cr,
                                      gtk_widget_get_allocated_width (widget),
                                      gtk_widget_get_allocated_height (widget));
       return FALSE;
     }
 
-  gtk_gl_area_make_current (self);
+  gtk_gl_area_make_current (area);
+
+  gtk_gl_area_attach_buffers (area);
+
+ if (priv->has_depth_buffer)
+   glEnable (GL_DEPTH_TEST);
+ else
+   glDisable (GL_DEPTH_TEST);
 
   scale = gtk_widget_get_scale_factor (widget);
   w = gtk_widget_get_allocated_width (widget) * scale;
   h = gtk_widget_get_allocated_height (widget) * scale;
-
-  if (priv->has_alpha)
-    {
-      /* For alpha we use textures as that is required for blending to work */
-      glGenTextures (1, &color_tex);
-      glBindTexture (GL_TEXTURE_2D, color_tex);
-      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-      glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-      glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
-                                 GL_TEXTURE_2D, color_tex, 0);
-    }
-  else
-    {
-      /* For non-alpha we use render buffers so we can blit instead of texture the result */
-      glGenRenderbuffersEXT (1, &color_rb);
-      glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, color_rb);
-      glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGB8, w, h);
-      glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
-                                    GL_RENDERBUFFER_EXT, color_rb);
-    }
-
-  if (priv->has_depth_buffer)
-    {
-      glGenRenderbuffersEXT (1, &depth_rb);
-      glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, depth_rb);
-      /* TODO: Pick actual requested depth */
-      glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
-      glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
-                                   GL_RENDERBUFFER_EXT, depth_rb);
-      glEnable (GL_DEPTH_TEST);
-    }
-  else
-    glDisable (GL_DEPTH_TEST);
-
   status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
   if (status ==  GL_FRAMEBUFFER_COMPLETE_EXT)
     {
-      glViewport(0, 0, w, h);
-
-      g_signal_emit (self, area_signals[RENDER], 0, priv->context, &unused);
+      if (priv->needs_render || priv->auto_render)
+       {
+         glViewport(0, 0, w, h);
+         g_signal_emit (area, area_signals[RENDER], 0, priv->context, &unused);
+       }
+      priv->needs_render = FALSE;
 
       gdk_cairo_draw_from_gl (cr,
                               gtk_widget_get_window (widget),
-                              color_tex ? color_tex : color_rb,
-                              color_tex ? GL_TEXTURE : GL_RENDERBUFFER,
+                              priv->texture ? priv->texture : priv->render_buffer,
+                              priv->texture ? GL_TEXTURE : GL_RENDERBUFFER,
                               scale, 0, 0, w, h);
-
-      gtk_gl_area_make_current (self);
+      gtk_gl_area_make_current (area);
+      gtk_gl_area_post_render (area);
     }
   else
     {
       g_print ("fb setup not supported\n");
     }
 
-  if (color_tex != 0)
-    glDeleteTextures(1, &color_tex);
-
-  if (color_rb != 0)
-    glDeleteRenderbuffersEXT(1, &color_rb);
-
-  if (depth_rb != 0)
-    glDeleteRenderbuffersEXT(1, &depth_rb);
-
   return TRUE;
 }
 
@@ -414,6 +597,26 @@ gtk_gl_area_class_init (GtkGLAreaClass *klass)
                          G_PARAM_READABLE |
                          G_PARAM_STATIC_STRINGS);
 
+  /**
+   * GtkGLArea:auto-render:
+   *
+   * If set to %TRUE the #GtkGLArea::render signal will be emitted every time the widget
+   * draws. This is the default and is useful if drawing the widget is fastr.
+   *
+   * If set to %FALSE the data from previous rendering is kept around and will be used
+   * for drawing the widget the next time, unless the window is resized. In order to
+   * force a rendering gtk_gl_area_queue_render() must be called. This mode is useful
+   * when the scene changes seldom, but takes a long time to redraw.
+   *
+   * Since: 3.16
+   */
+  obj_props[PROP_AUTO_RENDER] =
+    g_param_spec_boolean ("auto-render",
+                          P_("Auto render"),
+                          P_("Whether the gl area renders on each redraw"),
+                          TRUE,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
   /**
    * GtkGLArea:has-alpha:
    *
@@ -448,6 +651,21 @@ gtk_gl_area_class_init (GtkGLAreaClass *klass)
                           FALSE,
                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
+  /**
+   * GtkGLArea:has-stencil-buffer:
+   *
+   * If set to %TRUE the widget will allocate and enable a stencil buffer for the target
+   * framebuffer.
+   *
+   * Since: 3.16
+   */
+  obj_props[PROP_HAS_STENCIL_BUFFER] =
+    g_param_spec_boolean ("has-stencil-buffer",
+                          P_("Has stencil buffer"),
+                          P_("Whether a stencil buffer is allocated"),
+                          FALSE,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
   gobject_class->set_property = gtk_gl_area_set_property;
   gobject_class->get_property = gtk_gl_area_get_property;
   gobject_class->dispose = gtk_gl_area_dispose;
@@ -482,10 +700,15 @@ gtk_gl_area_class_init (GtkGLAreaClass *klass)
 }
 
 static void
-gtk_gl_area_init (GtkGLArea *self)
+gtk_gl_area_init (GtkGLArea *area)
 {
-  gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
-  gtk_widget_set_app_paintable (GTK_WIDGET (self), TRUE);
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  gtk_widget_set_has_window (GTK_WIDGET (area), FALSE);
+  gtk_widget_set_app_paintable (GTK_WIDGET (area), TRUE);
+
+  priv->auto_render = TRUE;
+  priv->needs_render = TRUE;
 }
 
 /**
@@ -552,6 +775,8 @@ gtk_gl_area_set_has_alpha (GtkGLArea *area,
       priv->has_alpha = has_alpha;
 
       g_object_notify (G_OBJECT (area), "has-alpha");
+
+      gtk_gl_area_maybe_allocate_buffers (area);
     }
 }
 
@@ -601,6 +826,144 @@ gtk_gl_area_set_has_depth_buffer (GtkGLArea *area,
       priv->has_depth_buffer = has_depth_buffer;
 
       g_object_notify (G_OBJECT (area), "has-depth-buffer");
+
+      gtk_gl_area_maybe_allocate_buffers (area);
+    }
+}
+
+/**
+ * gtk_gl_area_get_has_stencil_buffer:
+ * @area: a #GtkGLArea
+ *
+ * Returns whether the area has a stencil buffer.
+ *
+ * Returns: %TRUE if the @area has a stencil buffer, %FALSE otherwise
+ *
+ * Since: 3.16
+ */
+gboolean
+gtk_gl_area_get_has_stencil_buffer (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
+
+  return priv->has_stencil_buffer;
+}
+
+/**
+ * gtk_gl_area_set_has_stencil_buffer:
+ * @area: a #GtkGLArea
+ * @has_stencil_buffer: %TRUE to add a stencil buffer
+ *
+ * If @has_stencil_buffer is %TRUE the widget will allocate and
+ * enable a stencil buffer for the target framebuffer. Otherwise
+ * there will be none.
+ *
+ * Since: 3.16
+ */
+void
+gtk_gl_area_set_has_stencil_buffer (GtkGLArea *area,
+                                   gboolean   has_stencil_buffer)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  g_return_if_fail (GTK_IS_GL_AREA (area));
+
+  has_stencil_buffer = !!has_stencil_buffer;
+
+  if (priv->has_stencil_buffer != has_stencil_buffer)
+    {
+      priv->has_stencil_buffer = has_stencil_buffer;
+
+      g_object_notify (G_OBJECT (area), "has-stencil-buffer");
+
+      gtk_gl_area_maybe_allocate_buffers (area);
+    }
+}
+
+/**
+ * gtk_gl_area_queue_render:
+ * @area: a #GtkGLArea
+ *
+ * Marks the currently rendered data (if any) as invalid, and queues a
+ * redraw of the widget, ensuring that the #GtkGLArea::render signal
+ * is emitted during the draw.
+ *
+ * This is only needed when the gtk_gl_area_set_auto_render() has
+ * been called with a %FALSE value. The default behaviour is to
+ * emit #GtkGLArea::render on each draw.
+ *
+ * Since: 3.16
+ */
+void
+gtk_gl_area_queue_render (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  g_return_if_fail (GTK_IS_GL_AREA (area));
+
+  priv->needs_render = TRUE;
+
+  gtk_widget_queue_draw (GTK_WIDGET (area));
+}
+
+
+/**
+ * gtk_gl_area_get_auto_render:
+ * @area: a #GtkGLArea
+ *
+ * Returns whether the area is in auto render mode or not.
+ *
+ * Returns: %TRUE if the @area is auto rendering, %FALSE otherwise
+ *
+ * Since: 3.16
+ */
+gboolean
+gtk_gl_area_get_auto_render (GtkGLArea *area)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
+
+  return priv->auto_render;
+}
+
+/**
+ * gtk_gl_area_set_auto_render:
+ * @area: a #GtkGLArea
+ * @auto_render: a boolean
+ *
+ * If @auto_render is %TRUE the #GtkGLArea::render signal will be
+ * emitted every time the widget draws. This is the default and is
+ * useful if drawing the widget is fastr.
+ *
+ * If @auto_render is %FALSE the data from previous rendering is kept
+ * around and will be used for drawing the widget the next time,
+ * unless the window is resized. In order to force a rendering
+ * gtk_gl_area_queue_render() must be called. This mode is useful when
+ * the scene changes seldom, but takes a long time to redraw.
+ *
+ * Since: 3.16
+ */
+void
+gtk_gl_area_set_auto_render (GtkGLArea *area,
+                            gboolean   auto_render)
+{
+  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+  g_return_if_fail (GTK_IS_GL_AREA (area));
+
+  auto_render = !!auto_render;
+
+  if (priv->auto_render != auto_render)
+    {
+      priv->auto_render = auto_render;
+
+      g_object_notify (G_OBJECT (area), "auto-render");
+
+      if (auto_render)
+       gtk_widget_queue_draw (GTK_WIDGET (area));
     }
 }
 
@@ -632,8 +995,8 @@ gtk_gl_area_get_context (GtkGLArea *area)
  * the #GtkGLArea.
  *
  * This function is automatically called before emitting the
- * #GtkGLArea::render signal, and should not be called by
- * application code.
+ * #GtkGLArea::render signal, and doesn't normally need to be called
+ * by application code.
  *
  * Since: 3.16
  */
index 559389a029aae17c6937132d85e769883b9a8f04..511cede12ed9a175793700f85919507d98a1f936 100644 (file)
@@ -94,11 +94,30 @@ GDK_AVAILABLE_IN_3_16
 void            gtk_gl_area_set_has_depth_buffer (GtkGLArea        *area,
                                                   gboolean          has_depth_buffer);
 
+GDK_AVAILABLE_IN_3_16
+gboolean        gtk_gl_area_get_has_stencil_buffer (GtkGLArea        *area);
+
+GDK_AVAILABLE_IN_3_16
+void            gtk_gl_area_set_has_stencil_buffer (GtkGLArea        *area,
+                                                   gboolean          has_stencil_buffer);
+
+GDK_AVAILABLE_IN_3_16
+gboolean       gtk_gl_area_get_auto_render (GtkGLArea *area);
+GDK_AVAILABLE_IN_3_16
+void           gtk_gl_area_set_auto_render (GtkGLArea *area,
+                                           gboolean   auto_render);
+GDK_AVAILABLE_IN_3_16
+void           gtk_gl_area_queue_render    (GtkGLArea *area);
+
+
 GDK_AVAILABLE_IN_3_16
 GdkGLContext *  gtk_gl_area_get_context         (GtkGLArea        *area);
 
 GDK_AVAILABLE_IN_3_16
 void            gtk_gl_area_make_current        (GtkGLArea        *area);
+GDK_AVAILABLE_IN_3_16
+void            gtk_gl_area_attach_buffers      (GtkGLArea        *area);
+
 
 G_END_DECLS